home *** CD-ROM | disk | FTP | other *** search
-
- /***********************************************************
- * Node-Positioning for General Trees, by John Q. Walker II
- *
- * Initiated by calling procedure TreePosition().
- **********************************************************/
-
- #include <stdlib.h>
-
- /*----------------------------------------------------------
- * Implementation dependent: Set the values for each of
- * these variables.
- *--------------------------------------------------------*/
- #define NODE_WIDTH 20 /* Width of a node? */
- #define NODE_HEIGHT 10 /* Height of a node? */
- #define FRAME_THICKNESS 1 /* Fixed-sized node frame?*/
- #define SUBTREE_SEPARATION 5 /* Gap between subtrees? */
- #define SIBLING_SEPARATION 4 /* Gap between siblings? */
- #define LEVEL_SEPARATION 5 /* Gap between levels? */
- #define MAXIMUM_DEPTH 10 /* Biggest tree? */
-
- /*----------------------------------------------------------
- * Implementation dependent: The structure for one node
- * - The first 4 pointers must be set for each node before
- * this algorithm is called.
- * - The X and Y coordinates must be set for only the apex
- * of the tree upon entry; they will be set by this
- * algorithm for all the other nodes.
- * - The next three elements are used only for the duration
- * of the algorithm.
- * - Actual contents of the node depend on your application.
- *--------------------------------------------------------*/
- typedef int COORD; /* X,Y coordinate type */
-
- typedef struct node {
- struct node *parent; /* ptr: node's parent */
- struct node *offspring; /* ptr: leftmost child */
- struct node *leftsibling; /* ptr: sibling on left */
- struct node *rightsibling; /* ptr: sibling on right */
- COORD xCoordinate; /* node's current x coord */
- COORD yCoordinate; /* node's current y coord */
-
- struct node *prev; /* ptr: lefthand neighbor */
- float flPrelim; /* preliminary x coord */
- float flModifier; /* temporary modifier */
-
- char info[80]; /* pick your contents! */
- } *PNODE; /* ptr: a node structure */
-
- /*----------------------------------------------------------
- * Global variables used by the algorithm
- *--------------------------------------------------------*/
- typedef enum { FALSE, TRUE } BOOL;
- typedef enum { FRAME, NO_FRAME } FRAME_TYPE;
- typedef enum { NORTH, SOUTH, EAST, WEST } ROOT_ORIENTATION;
-
- typedef struct prev_node { /* one list element */
- PNODE pPreviousNode; /* ptr: previous at level */
- struct prev_node *pNextLevel; /* ptr: next element */
- } *PPREVIOUS_NODE;
-
- static FRAME_TYPE FrameType = FRAME; /* Show a frame */
- static ROOT_ORIENTATION RootOrientation = NORTH; /* At top*/
- static PPREVIOUS_NODE pLevelZero = (PPREVIOUS_NODE)0;
-
- static COORD xTopAdjustment; /* How to adjust the apex */
- static COORD yTopAdjustment; /* How to adjust the apex */
- static float flMeanWidth; /* Ave. width of 2 nodes */
-
- #define FIRST_TIME (0) /* recursive proc flag */
-
- /*----------------------------------------------------------
- * Implemented as macros, but could be implemented as
- * procedures depending on your particular node structures
- *--------------------------------------------------------*/
- #define FirstChild(node) ((PNODE)((node)->offspring))
- #define LeftSibling(node) ((PNODE)((node)->leftsibling))
- #define RightSibling(node) ((PNODE)((node)->rightsibling))
- #define Parent(node) ((PNODE)((node)->parent))
- #define LeftNeighbor(node) ((PNODE)((node)->prev))
- #define IsLeaf(node) \
- (((node)&&(!((node)->offspring)))?TRUE:FALSE)
- #define HasChild(node) \
- (((node)&&((node)->offspring))?TRUE:FALSE)
- #define HasLeftSibling(node) \
- (((node)&&((node)->leftsibling))?TRUE:FALSE)
- #define HasRightSibling(node) \
- (((node)&&((node)->rightsibling))?TRUE:FALSE)
-
-
- static PNODE GetPrevNodeAtLevel (unsigned nLevelNbr)
- {
- /*------------------------------------------------------
- * List Manipulation: Return pointer to previous node at
- * this level
- *----------------------------------------------------*/
- PPREVIOUS_NODE pTempNode; /* used in the for-loop */
- unsigned i = 0; /* level counter */
-
- for (pTempNode = pLevelZero; (pTempNode);
- pTempNode = pTempNode->pNextLevel) {
- if (i++ == nLevelNbr)
- /* Reached desired level. Return its pointer */
- return (pTempNode->pPreviousNode);
- }
- return ((PNODE)0); /* No pointer yet for this level. */
- }
-
- static BOOL SetPrevNodeAtLevel (unsigned nLevelNbr,
- PNODE pThisNode)
- {
- /*------------------------------------------------------
- * List Manipulation: Set the list element to the
- * previous node at this level
- *----------------------------------------------------*/
-
- PPREVIOUS_NODE pTempNode; /* used in the for-loop */
- PPREVIOUS_NODE pNewNode; /* newly-allocated memory */
- unsigned i = 0; /* level counter */
-
- for (pTempNode = pLevelZero; (pTempNode);
- pTempNode = pTempNode->pNextLevel) {
- if (i++ == nLevelNbr) {
- /* Reached desired level. Return its pointer */
- pTempNode->pPreviousNode = pThisNode;
- return (TRUE);
- }
- else if (pTempNode->pNextLevel==(PPREVIOUS_NODE)0) {
- /* Looks like we need a new level: add it. */
- /* We need to keep going--should be the next. */
- pNewNode = (PPREVIOUS_NODE)
- malloc(sizeof(struct prev_node));
- if (pNewNode) {
- pNewNode->pPreviousNode = (PNODE)0;
- pNewNode->pNextLevel = (PPREVIOUS_NODE)0;
- pTempNode->pNextLevel = pNewNode;
- }
- else return (FALSE); /* The malloc failed. */
- }
- }
-
- /* Should only get here if pLevelZero is 0. */
- pLevelZero = (PPREVIOUS_NODE)
- malloc(sizeof(struct prev_node));
- if (pLevelZero) {
- pLevelZero->pPreviousNode = pThisNode;
- pLevelZero->pNextLevel = (PPREVIOUS_NODE)0;
- return (TRUE);
- }
- else return (FALSE); /* The malloc() failed. */
- }
-
- static void InitPrevNodeAtLevel (void)
- {
- /*------------------------------------------------------
- * List Manipulation: Initialize the list of the
- * previous node at each level to all zeros.
- *----------------------------------------------------*/
-
- PPREVIOUS_NODE pTempNode = pLevelZero; /* the start */
-
- for ( ; (pTempNode); pTempNode = pTempNode->pNextLevel)
- pTempNode->pPreviousNode = (PNODE)0;
- }
-
- static BOOL CheckExtentsRange(long lxTemp, long lyTemp)
- {
- /*------------------------------------------------------
- * Insert your own code here, to check when the
- * tree drawing is going to be too big. My region is no
- * more that 64000 units square.
- *----------------------------------------------------*/
-
- if ((labs(lxTemp) > 32000) || (labs(lyTemp) > 32000))
- return (FALSE);
- else
- return (TRUE);
- }
-
- static void TreeMeanNodeSize (PNODE pLeftNode,
- PNODE pRightNode)
- {
- /*------------------------------------------------------
- * Write your own code for this procedure if your
- * rendered nodes will have variable sizes.
- *------------------------------------------------------
- * Here I add the width of the contents of the
- * right half of the pLeftNode to the left half of the
- * right node. Since the size of the contents for all
- * nodes is currently the same, this module computes the
- * following trivial computation.
- *----------------------------------------------------*/
-
- flMeanWidth = (float)0.0; /* Initialize this global */
-
- switch (RootOrientation) {
- case NORTH:
- case SOUTH:
- if (pLeftNode) {
- flMeanWidth = flMeanWidth +
- (float)(NODE_WIDTH/2);
- if (FrameType != NO_FRAME)
- flMeanWidth = flMeanWidth +
- (float)FRAME_THICKNESS;
- }
- if (pRightNode) {
- flMeanWidth = flMeanWidth +
- (float)(NODE_WIDTH/2);
- if (FrameType != NO_FRAME)
- flMeanWidth = flMeanWidth +
- (float)FRAME_THICKNESS;
- }
- break;
- case EAST :
- case WEST :
- if (pLeftNode) {
- flMeanWidth = flMeanWidth +
- (float)(NODE_HEIGHT/2);
- if (FrameType != NO_FRAME)
- flMeanWidth = flMeanWidth +
- (float)FRAME_THICKNESS;
- }
- if (pRightNode) {
- flMeanWidth = flMeanWidth +
- (float)(NODE_HEIGHT/2);
- if (FrameType != NO_FRAME)
- flMeanWidth = flMeanWidth +
- (float)FRAME_THICKNESS;
- }
- break;
- }
- }
-
- static PNODE TreeGetLeftmost(PNODE pThisNode,
- unsigned nCurrentLevel,
- unsigned nSearchDepth)
- {
- /*------------------------------------------------------
- * Determine the leftmost descendant of a node at a
- * given depth. This is implemented using a post-order
- * walk of the subtree under pThisNode, down to the
- * level of nSearchDepth. If we've searched to the
- * proper distance, return the currently leftmost node.
- * Otherwise, recursively look at the progressively
- * lower levels.
- *----------------------------------------------------*/
-
- PNODE pLeftmost; /* leftmost descendant at level */
- PNODE pRightmost; /* rightmost offspring in search */
-
- if (nCurrentLevel == nSearchDepth)
- pLeftmost = pThisNode; /* searched far enough. */
- else if (IsLeaf(pThisNode))
- pLeftmost = 0; /* This node has no descendants */
- else { /* Do a post-order walk of the subtree. */
- for (pLeftmost = TreeGetLeftmost(pRightmost =
- FirstChild(pThisNode),
- nCurrentLevel + 1,
- nSearchDepth)
- ;
- (pLeftmost==0) && (HasRightSibling(pRightmost))
- ;
- pLeftmost = TreeGetLeftmost(pRightmost =
- RightSibling(pRightmost),
- nCurrentLevel + 1,
- nSearchDepth)
- ) { /* Nothing inside this for-loop. */ }
- }
- return (pLeftmost);
- }
-
- static void TreeApportion (PNODE pThisNode,
- unsigned nCurrentLevel)
- {
- /*------------------------------------------------------
- * Clean up the positioning of small sibling subtrees.
- * Subtrees of a node are formed independently and
- * placed as close together as possible. By requiring
- * that the subtrees be rigid at the time they are put
- * together, we avoid the undesirable effects that can
- * accrue from positioning nodes rather than subtrees.
- *----------------------------------------------------*/
-
- PNODE pLeftmost; /* leftmost at given level*/
- PNODE pNeighbor; /* node left of pLeftmost */
- PNODE pAncestorLeftmost; /* ancestor of pLeftmost */
- PNODE pAncestorNeighbor; /* ancestor of pNeighbor */
- PNODE pTempPtr; /* loop control pointer */
- unsigned i; /* loop control */
- unsigned nCompareDepth; /* depth of comparison */
- /* within this proc */
- unsigned nDepthToStop; /* depth to halt */
- unsigned nLeftSiblings; /* nbr of siblings to the */
- /* left of pThisNode, including pThisNode, */
- /* til the ancestor of pNeighbor */
- float flLeftModsum; /* sum of ancestral mods */
- float flRightModsum; /* sum of ancestral mods */
- float flDistance; /* difference between */
- /* where pNeighbor thinks pLeftmost should be */
- /* and where pLeftmost actually is */
- float flPortion; /* proportion of */
- /* flDistance to be added to each sibling */
-
- pLeftmost = FirstChild(pThisNode);
- pNeighbor = LeftNeighbor(pLeftmost);
-
- nCompareDepth = 1;
- nDepthToStop = MAXIMUM_DEPTH - nCurrentLevel;
-
- while ((pLeftmost) && (pNeighbor) &&
- (nCompareDepth <= nDepthToStop)) {
-
- /* Compute the location of pLeftmost and where it */
- /* should be with respect to pNeighbor. */
- flRightModsum = flLeftModsum = (float)0.0;
- pAncestorLeftmost = pLeftmost;
- pAncestorNeighbor = pNeighbor;
- for (i = 0; (i < nCompareDepth); i++) {
- pAncestorLeftmost = Parent(pAncestorLeftmost);
- pAncestorNeighbor = Parent(pAncestorNeighbor);
- flRightModsum = flRightModsum +
- pAncestorLeftmost->flModifier;
- flLeftModsum = flLeftModsum +
- pAncestorNeighbor->flModifier;
- }
-
- /* Determine the flDistance to be moved, and apply*/
- /* it to "pThisNode's" subtree. Apply appropriate*/
- /* portions to smaller interior subtrees */
-
- /* Set the global mean width of these two nodes */
- TreeMeanNodeSize(pLeftmost, pNeighbor);
-
- flDistance = (pNeighbor->flPrelim +
- flLeftModsum +
- (float)SUBTREE_SEPARATION +
- (float)flMeanWidth) -
- (pLeftmost->flPrelim + flRightModsum);
-
- if (flDistance > (float)0.0) {
- /* Count the interior sibling subtrees */
- nLeftSiblings = 0;
- for (pTempPtr = pThisNode;
- (pTempPtr) &&
- (pTempPtr != pAncestorNeighbor);
- pTempPtr = LeftSibling(pTempPtr)) {
- nLeftSiblings++;
- }
-
- if (pTempPtr) {
- /* Apply portions to appropriate */
- /* leftsibling subtrees. */
- flPortion = flDistance/(float)nLeftSiblings;
- for (pTempPtr = pThisNode;
- (pTempPtr != pAncestorNeighbor);
- pTempPtr = LeftSibling(pTempPtr)) {
- pTempPtr->flPrelim =
- pTempPtr->flPrelim + flDistance;
- pTempPtr->flModifier =
- pTempPtr->flModifier + flDistance;
- flDistance = flDistance - flPortion;
- }
- }
- else {
- /* Don't need to move anything--it needs */
- /* to be done by an ancestor because */
- /* pAncestorNeighbor and */
- /* pAncestorLeftmost are not siblings of */
- /* each other. */
- return;
- }
- } /* end of the while */
-
- /* Determine the leftmost descendant of pThisNode */
- /* at the next lower level to compare its */
- /* positioning against that of its pNeighbor. */
- nCompareDepth++;
- if (IsLeaf(pLeftmost))
- pLeftmost = TreeGetLeftmost(pThisNode, 0,
- nCompareDepth);
- else
- pLeftmost = FirstChild(pLeftmost);
- pNeighbor = LeftNeighbor(pLeftmost);
- }
- }
-
- static BOOL TreeFirstWalk(PNODE pThisNode,
- unsigned nCurrentLevel)
- {
- /*------------------------------------------------------
- * In a first post-order walk, every node of the tree is
- * assigned a preliminary x-coordinate (held in field
- * node->flPrelim). In addition, internal nodes are
- * given modifiers, which will be used to move their
- * children to the right (held in field
- * node->flModifier).
- * Returns: TRUE if no errors, otherwise returns FALSE.
- *----------------------------------------------------*/
-
- PNODE pLeftmost; /* left- & rightmost */
- PNODE pRightmost; /* children of a node. */
- float flMidpoint; /* midpoint between left- */
- /* & rightmost children */
-
- /* Set up the pointer to previous node at this level */
- pThisNode->prev = GetPrevNodeAtLevel(nCurrentLevel);
-
- /* Now we're it--the previous node at this level */
- if (!(SetPrevNodeAtLevel(nCurrentLevel, pThisNode)))
- return (FALSE); /* Can't allocate element */
-
- /* Clean up old values in a node's flModifier */
- pThisNode->flModifier = (float)0.0;
-
- if ((IsLeaf(pThisNode)) ||
- (nCurrentLevel == MAXIMUM_DEPTH)) {
- if (HasLeftSibling(pThisNode)) {
- /*---------------------------------------------
- * Determine the preliminary x-coordinate
- * based on:
- * - preliminary x-coordinate of left sibling,
- * - the separation between sibling nodes, and
- * - mean width of left sibling & current node.
- *--------------------------------------------*/
- /* Set the mean width of these two nodes */
- TreeMeanNodeSize(LeftSibling(pThisNode),
- pThisNode);
-
- pThisNode->flPrelim =
- (pThisNode->leftsibling->flPrelim) +
- (float)SIBLING_SEPARATION +
- flMeanWidth;
- }
- else /* no sibling on the left to worry about */
- pThisNode->flPrelim = (float)0.0;
- }
- else {
- /* Position the leftmost of the children */
- if (TreeFirstWalk(pLeftmost = pRightmost =
- FirstChild(pThisNode),
- nCurrentLevel + 1)) {
- /* Position each of its siblings to its right */
- while (HasRightSibling(pRightmost)) {
- if (TreeFirstWalk(pRightmost =
- RightSibling(pRightmost),
- nCurrentLevel + 1)) {
- }
- else return (FALSE); /* malloc() failed */
- }
-
- /* Calculate the preliminary value between */
- /* the children at the far left and right */
- flMidpoint = (pLeftmost->flPrelim +
- pRightmost->flPrelim)/(2.0);
-
- /* Set global mean width of these two nodes */
- TreeMeanNodeSize(LeftSibling(pThisNode),
- pThisNode);
-
- if (HasLeftSibling(pThisNode)) {
- pThisNode->flPrelim =
- (pThisNode->leftsibling->flPrelim) +
- (float)SIBLING_SEPARATION +
- flMeanWidth;
-
- pThisNode->flModifier =
- pThisNode->flPrelim - flMidpoint;
- TreeApportion(pThisNode, nCurrentLevel);
- }
- else pThisNode->flPrelim = flMidpoint;
- }
- else return (FALSE); /* Couldn't get an element */
- }
- return (TRUE);
- }
-
- static BOOL TreeSecondWalk(PNODE pThisNode,
- unsigned nCurrentLevel)
- {
- /*------------------------------------------------------
- * During a second pre-order walk, each node is given a
- * final x-coordinate by summing its preliminary
- * x-coordinate and the modifiers of all the node's
- * ancestors. The y-coordinate depends on the height of
- * the tree. (The roles of x and y are reversed for
- * RootOrientations of EAST or WEST.)
- * Returns: TRUE if no errors, otherwise returns FALSE.
- *----------------------------------------------------*/
-
- BOOL bResult = TRUE; /* assume innocent */
- long lxTemp, lyTemp; /* hold calculations here */
- float flNewModsum; /* local modifier value */
- static float flModsum = (float)0.0;
-
- if (nCurrentLevel <= MAXIMUM_DEPTH) {
- flNewModsum = flModsum; /* Save the current value */
- switch (RootOrientation) {
- case NORTH:
- lxTemp = (long)xTopAdjustment +
- (long)(pThisNode->flPrelim + flModsum);
- lyTemp = (long)yTopAdjustment +
- (long)(nCurrentLevel * LEVEL_SEPARATION);
- break;
- case SOUTH:
- lxTemp = (long)xTopAdjustment +
- (long)(pThisNode->flPrelim + flModsum);
- lyTemp = (long)yTopAdjustment -
- (long)(nCurrentLevel * LEVEL_SEPARATION);
- break;
- case EAST:
- lxTemp = (long)xTopAdjustment +
- (long)(nCurrentLevel * LEVEL_SEPARATION);
- lyTemp = (long)yTopAdjustment -
- (long)(pThisNode->flPrelim + flModsum);
- break;
- case WEST:
- lxTemp = (long)xTopAdjustment -
- (long)(nCurrentLevel * LEVEL_SEPARATION);
- lyTemp = (long)yTopAdjustment -
- (long)(pThisNode->flPrelim + flModsum);
- break;
- }
-
- if (CheckExtentsRange(lxTemp, lyTemp)) {
- /* The values are within the allowable range */
- pThisNode->xCoordinate = (COORD)lxTemp;
- pThisNode->yCoordinate = (COORD)lyTemp;
-
- if (HasChild(pThisNode)) {
- /* Apply the flModifier value for this */
- /* node to all its offspring. */
- flModsum = flNewModsum =
- flNewModsum + pThisNode->flModifier;
- bResult = TreeSecondWalk(
- FirstChild(pThisNode),nCurrentLevel + 1);
- flNewModsum = flNewModsum -
- pThisNode->flModifier;
- }
-
- if ((HasRightSibling(pThisNode)) && (bResult)) {
- flModsum = flNewModsum;
- bResult = TreeSecondWalk(
- RightSibling(pThisNode), nCurrentLevel);
- }
- }
- else bResult = FALSE; /* outside of extents */
- }
- return (bResult);
- }
-
- static BOOL TreePosition(PNODE pApexNode)
- {
- /*------------------------------------------------------
- * Determine the coordinates for each node in a tree.
- * Input: Pointer to the apex node of the tree
- * Assumption: The x & y coordinates of the apex node
- * are already correct, since the tree underneath it
- * will be positioned with respect to those coordinates.
- * Returns: TRUE if no errors, otherwise returns FALSE.
- *----------------------------------------------------*/
-
- if (pApexNode) {
- /* Initialize list of previous node at each level */
- InitPrevNodeAtLevel();
-
- /* Generate the properly-placed tree nodes. */
- /* TreeFirstWalk: a post-order walk */
- /* TreeSecondWalk: a pre-order walk */
- if (TreeFirstWalk (pApexNode, FIRST_TIME)) {
- /* Determine how to adjust the nodes with */
- /* respect to the location of the apex of the */
- /* tree being positioned. */
- switch (RootOrientation) {
- case NORTH:
- case SOUTH:
- /* Create the adjustment from x-coord */
- xTopAdjustment = pApexNode->xCoordinate -
- (COORD)(pApexNode->flPrelim);
- yTopAdjustment = pApexNode->yCoordinate;
- break;
- case EAST :
- case WEST :
- /* Create the adjustment from y-coord */
- xTopAdjustment = pApexNode->xCoordinate;
- yTopAdjustment = pApexNode->yCoordinate +
- (COORD)(pApexNode->flPrelim);
- break;
- }
- return (TreeSecondWalk(pApexNode, FIRST_TIME));
- }
- else return (FALSE); /* Couldn't get an element */
- }
- else return (TRUE); /* Easy: null pointer was passed */
- }
-